using System;
using System.Collections;
using System.Collections.Specialized;
using System.IO;
using System.Net;

/// <summary>
/// Provides functionality for programmatically downloading files 
/// with the HTTP protocol.
/// </summary>
public sealed class HttpDownload
{

    // Prevent construction
    private HttpDownload() { }

    /// <summary>
    /// Downloads the given file into a byte array from a url.
    /// </summary>
    /// <param name="pathname">The http path of the file to download</param>
    public static HttpWebResponse Download(Uri pathname)
    {
        // Initialize the request object
        HttpWebRequest req = (WebRequest.Create(pathname) as HttpWebRequest);
        req.Method = "GET";
        req.ContentType = "text/xml";
        req.Timeout = -1;

        return (req.GetResponse() as HttpWebResponse);
    }
}

  /// <summary>
  /// Provides functionality for programmatically uploading files 
  /// with the HTTP protocol.
  /// </summary>
  public sealed class HttpUpload
  {

    // Prevent construction
    private HttpUpload() { }

    /// <summary>
    /// Uploads the given data.
    /// </summary>
    /// <param name="uri">The URI to which the data shall be sent.</param>
    /// <param name="formFields">Form fields to be posted along with the file.</param>
    /// <param name="objects">The data to be sent.</param>
    public static HttpWebResponse Upload(Uri uri, StringDictionary formFields, params UploadSpec[] objects) {

      // Initialize the request object
      HttpWebRequest req = (WebRequest.Create(uri) as HttpWebRequest);
      
      string boundary = Guid.NewGuid().ToString().Replace("-", "");
      req.ContentType = "multipart/form-data; boundary=" + boundary;
      req.Method = "POST";

      MemoryStream postData = new MemoryStream();
      string newLine = "\r\n";
      StreamWriter sw = new StreamWriter(postData);

      if (formFields != null) 
        foreach (DictionaryEntry de in formFields) {
          sw.Write("--" + boundary + newLine);
          sw.Write("Content-Disposition: form-data; name=\"{0}\"{1}{1}{2}{1}",
            de.Key,
            newLine,
            de.Value
          );
        }
      
      foreach (UploadSpec us in objects) {
        sw.Write("--" + boundary + newLine);
        sw.Write("Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"{2}",
          us.FieldName,
          us.FileName,
          newLine
        );
        sw.Write("Content-Type: application/octet-stream" + newLine + newLine);
        sw.Flush();

        postData.Write(us.Contents, 0, us.Contents.Length);
        sw.Write(newLine);
      }

      sw.Write("--{0}--{1}", boundary, newLine);
      sw.Flush();

      req.ContentLength = postData.Length;
      using (Stream s = req.GetRequestStream())
        postData.WriteTo(s);
      postData.Close();

      return (req.GetResponse() as HttpWebResponse);
    }
  }

  /// <summary>
  /// Holds the information about the file(s) to be uploaded.
  /// </summary>
  public struct UploadSpec {
      
    private byte[] contents;
    private string fileName;
    private string fieldName;

    /// <summary>
    /// The byte array content to be uploaded.
    /// </summary>
    public byte[] Contents {
      get { return contents; }
      set { contents = value; }
    }

    /// <summary>
    /// The name of the file to be uploaded.
    /// </summary>
    public string FileName {
      get { return fileName; }
      set { fileName = value; }
    }

    /// <summary>
    /// The HTML form field the file should be uploaded into.
    /// </summary>
    public string FieldName {
      get { return fieldName; }
      set { fieldName = value; }
    }

    /// <summary>
    /// Creates a new upload spec based on a byte array.
    /// </summary>
    /// <param name="contents">The contents to be uploaded.</param>
    /// <param name="fileName">The file to be uploaded.</param>
    /// <param name="fieldName">The field name as which this file shall be sent to.</param>
    public UploadSpec(byte[] contents, string fileName, string fieldName) {
      this.contents = contents;
      this.fileName = fileName;
      this.fieldName = fieldName;
    }
  }

